package redis

import "errors"

type SetArgs struct {
	Value   any
	NX      bool  // Only set the key if it does not already exist.
	XX      bool  // Only set the key if it already exists.
	GET     bool  // Return the old string stored at key, or nil if key did not exist.
	EX      int   // seconds -- Set the specified expire time, in seconds.
	PX      int   // milliseconds -- Set the specified expire time, in milliseconds.
	EXAT    int64 // timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds.
	PXAT    int64 // timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds.
	KEEPTTL bool  // Retain the time to live associated with the key.
}

func (a *SetArgs) args() []any {
	args := []any{a.Value}
	if a.NX {
		args = append(args, "NX")
	} else if a.XX {
		args = append(args, "XX")
	}
	if a.GET {
		args = append(args, "GET")
	}
	if a.KEEPTTL {
		args = append(args, "KEEPTTL")
	} else if a.PX > 0 {
		args = append(args, "PX", a.PX)
	} else if a.EX > 0 {
		args = append(args, "EX", a.EX)
	} else if a.PXAT > 0 {
		args = append(args, "PXAT", a.PXAT)
	} else if a.EXAT > 0 {
		args = append(args, "EXAT", a.EXAT)
	}
	return args
}

func SetValue(value any) *SetArgs          { return &SetArgs{Value: value} }
func (a *SetArgs) Nx() *SetArgs            { a.NX = true; return a }
func (a *SetArgs) Xx() *SetArgs            { a.XX = true; return a }
func (a *SetArgs) Get() *SetArgs           { a.GET = true; return a }
func (a *SetArgs) Ex(sec int) *SetArgs     { a.EX = sec; return a }
func (a *SetArgs) Px(msec int) *SetArgs    { a.PX = msec; return a }
func (a *SetArgs) ExAt(ts int64) *SetArgs  { a.EXAT = ts; return a }
func (a *SetArgs) PxAt(mts int64) *SetArgs { a.PXAT = mts; return a }
func (a *SetArgs) KeepTTL() *SetArgs       { a.KEEPTTL = true; return a }

func (k *key) Set(value any) ValueResult {
	if args, ok := value.(*SetArgs); ok {
		return k.cmd("SET", append([]any{k.key}, args.args()...)...)
	}
	return k.cmd("SET", k.key, value)
}

func (k *key) Get() ValueResult {
	return k.cmd("GET", k.key)
}

func (k *key) GetDel() ValueResult {
	return k.cmd("GETDEL", k.key)
}

type GetArgs struct {
	EX      int   // seconds -- Set the specified expire time, in seconds.
	PX      int   // milliseconds -- Set the specified expire time, in milliseconds.
	EXAT    int64 // timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds.
	PXAT    int64 // timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds.
	Persist bool  // Remove the time to live associated with the key.
}

func (a *GetArgs) args() []any {
	args := []any{}
	if a.Persist {
		args = append(args, "PERSIST")
	} else if a.PX > 0 {
		args = append(args, "PX", a.PX)
	} else if a.EX > 0 {
		args = append(args, "EX", a.EX)
	} else if a.PXAT > 0 {
		args = append(args, "PXAT", a.PXAT)
	} else if a.EXAT > 0 {
		args = append(args, "EXAT", a.EXAT)
	}
	return args
}

func (k *key) GetEx(args *GetArgs) ValueResult {
	return k.cmd("GETEX", append([]any{k.key}, args.args()...)...)
}

func (k *key) GetSet(value any) ValueResult {
	return k.cmd("GETSET", k.key, value)
}

func (k *key) StrLen() IntResult {
	return k.cmd("STRLEN", k.key)
}

func (k *key) Append(value string) IntResult {
	return k.cmd("APPEND", k.key, value)
}

func (k *key) SetRange(offset int, value string) IntResult {
	return k.cmd("SETRANGE", k.key, offset, value)
}

func (k *key) GetRange(start, end int) StringResult {
	return k.cmd("GETRANGE", k.key, start, end)
}

func (k *key) Incr() IntResult {
	return k.cmd("INCR", k.key)
}

func (k *key) IncrBy(increment int) IntResult {
	return k.cmd("INCRBY", k.key, increment)
}

func (k *key) IncrByFloat(increment float64) FloatResult {
	return k.cmd("INCRBYFLOAT", k.key, increment)
}

func (k *key) Decr() IntResult {
	return k.cmd("DECR", k.key)
}

func (k *key) DecrBy(increment int) IntResult {
	return k.cmd("DECRBY", k.key, increment)
}

func (k *key) MSet(value ...any) BoolResult {
	if len(k.keys) != len(value) {
		return cmdErr(errors.New("MSET: expects value count same as keys count"))
	}
	var args []any
	for i := range k.keys {
		args = append(args, k.keys[i], value[i])
	}

	return k.cmd("MSET", args...)
}

func (k *key) MSetNx(value ...any) BoolResult {
	if len(k.keys) != len(value) {
		return cmdErr(errors.New("MSETNX: expects value count same as keys count"))
	}
	var args []any
	for i := range k.keys {
		args = append(args, k.keys[i], value[i])
	}

	return k.cmd("MSETNX", args...)
}

func (k *key) MGet() ListResult {
	args := make([]any, len(k.keys))
	for i, k := range k.keys {
		args[i] = k
	}
	return k.cmd("MGET", args...)
}
